package com.rtf.framework.monitor.export.jvm;

import com.rtf.framework.monitor.config.AppMonitorProperties;
import com.google.common.collect.Lists;
import io.prometheus.client.Collector;
import io.prometheus.client.GaugeMetricFamily;

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.util.ArrayList;
import java.util.List;

/**
 * Exports metrics about JVM thread areas.
 * <p>
 * Example usage:
 * <pre>
 * {@code
 *   new ThreadExports().register();
 * }
 * </pre>
 * Example metrics being exported:
 * <pre>
 *   jvm_threads_current{} 300
 *   jvm_threads_daemon{} 200
 *   jvm_threads_peak{} 410
 *   jvm_threads_started_total{} 1200
 * </pre>
 */
public class AppThreadExports extends Collector {
  private final ThreadMXBean threadBean;

  public AppThreadExports() {
    this(ManagementFactory.getThreadMXBean());
  }

  public AppThreadExports(ThreadMXBean threadBean) {
    this.threadBean = threadBean;
  }

  void addThreadMetrics(List<MetricFamilySamples> sampleFamilies) {
    List<String> labels = Lists.newArrayList("appName") ;
    // 添加线程池数量
    GaugeMetricFamily jvmThreadsCurrent = new GaugeMetricFamily(
            "jvm_threads_current",
            "Current thread count of a JVM",
            labels ) ;
    jvmThreadsCurrent.addMetric(Lists.newArrayList(AppMonitorProperties.getAppName()) , threadBean.getThreadCount() ) ;
    sampleFamilies.add( jvmThreadsCurrent );

    // 后台运行线程
    GaugeMetricFamily jvmThreadsDaemon = new GaugeMetricFamily(
            "jvm_threads_daemon",
            "Daemon thread count of a JVM",
            labels ) ;
    jvmThreadsDaemon.addMetric(Lists.newArrayList(AppMonitorProperties.getAppName()) , threadBean.getDaemonThreadCount() ) ;
    sampleFamilies.add( jvmThreadsDaemon );

    // 线程峰值
    GaugeMetricFamily jvmThreadsPeak = new GaugeMetricFamily(
            "jvm_threads_peak",
            "Peak thread count of a JVM",
            labels ) ;
    jvmThreadsPeak.addMetric(Lists.newArrayList(AppMonitorProperties.getAppName()) , threadBean.getPeakThreadCount() ) ;
    sampleFamilies.add( jvmThreadsPeak );

    // 死锁线程
    GaugeMetricFamily jvmThreadsDeadlocked = new GaugeMetricFamily(
            "jvm_threads_deadlocked",
            "Cycles of JVM-threads that are in deadlock waiting to acquire object monitors or ownable synchronizers",
            labels ) ;
    jvmThreadsDeadlocked.addMetric(Lists.newArrayList(AppMonitorProperties.getAppName()) , nullSafeArrayLength(threadBean.findDeadlockedThreads()) ) ;
    sampleFamilies.add( jvmThreadsDeadlocked );

    // 等待线程
    GaugeMetricFamily jvmThreadsDeadlockedMonitor = new GaugeMetricFamily(
            "jvm_threads_deadlocked_monitor",
            "Cycles of JVM-threads that are in deadlock waiting to acquire object monitors",
            labels ) ;
    jvmThreadsDeadlockedMonitor.addMetric(Lists.newArrayList(AppMonitorProperties.getAppName()) , nullSafeArrayLength(threadBean.findMonitorDeadlockedThreads()) ) ;
    sampleFamilies.add( jvmThreadsDeadlockedMonitor );
  }

  private static double nullSafeArrayLength(long[] array) {
    return null == array ? 0 : array.length;
  }

  @Override
  public List<MetricFamilySamples> collect() {
    List<MetricFamilySamples> mfs = new ArrayList<MetricFamilySamples>();
    addThreadMetrics(mfs);
    return mfs;
  }
}
